/**
 * @file gpio.c
 * @brief T1 GPIO  Driver
 *
 * @author yemt@witsi.cn
 * @date 2011-08-13
 * @version T1.GPIO.02
 */
#include "regs.h"
#include "gpio.h"
#include "config.h"
#include "dev.h"
#include "ictl.h"
#include "init.h"

#define GPIO_PORT(n)    (((n) >> 5) & 31)       /**< Get the GPIO Port */
#define GPIO_PIN(n)     ((n) & 31)              /**< Get the GPIO Index */

/**
 * @fn gpio_set_output
 * @brief Set GPIO to Output IO
 *
 * @param gpio_num      GPIO PIN Number
 * @param attr          Reserved
 * @return void
 */
void gpio_set_output(uint32_t gpio_num, uint32_t attr)
{
        GPIO[GPIO_PORT(gpio_num)].GPIO_DIR |= (1 << GPIO_PIN(gpio_num));
}

/**
 * @fn gpio_set_input
 * @brief Set GPIO to Input IO
 *
 * @param gpio_num      GPIO PIN Number
 * @param attr          Reserved
 * @return void
 */
void gpio_set_input(uint32_t gpio_num, uint32_t attr)
{
        GPIO[GPIO_PORT(gpio_num)].GPIO_DIR &= ~(1 << GPIO_PIN(gpio_num));
}

/**
 * @fn gpio_input
 * @brief Get GPIO Input Value
 *
 * @param gpio_num      GPIO PIN Number
 * @return
 *      1:      High
 *      0:      Low
 */
int gpio_input(uint32_t gpio_num)
{
        return ((GPIO[GPIO_PORT(gpio_num)].GPIO_EXT & (1 << GPIO_PIN(gpio_num)))) ? 1 : 0;
}

/**
 * @fn gpio_output
 * @brief Set GPIO Output Value
 *
 * @param gpio_num      GPIO PIN Number
 * @param val           0 Low or !0 High
 * @return void
 */
void gpio_output(uint32_t gpio_num, int val)
{
        if (val)
                GPIO[GPIO_PORT(gpio_num)].GPIO_DATA |= (1 << GPIO_PIN(gpio_num));
        else
                GPIO[GPIO_PORT(gpio_num)].GPIO_DATA &= ~(1 << GPIO_PIN(gpio_num));
}


static gpio_irq_t gpio_handler[GPIO_MAX]; /**< GPIO Interrupt Handler */

/**
 * @fn gpio_irq_enable
 * @brief Enable GPIO Interrupt
 *
 * @param gpio_num      GPIO PIN Number
 * @return void
 */
void gpio_irq_enable(uint32_t gpio_num)
{
        GPIO[GPIO_PORT(gpio_num)].GPIO_IEN |= (1 << GPIO_PIN(gpio_num));
}

/**
 * @fn gpio_irq_disable
 * @brief Disable GPIO Interrupt
 *
 * @param gpio_num      GPIO PIN Number
 * @return void
 */
void gpio_irq_disable(uint32_t gpio_num)
{
        GPIO[GPIO_PORT(gpio_num)].GPIO_IEN &= ~(1 << GPIO_PIN(gpio_num));
}

/**
 * @fn gpio_request_irq
 * @brief Request GPIO Interrupt
 *
 * @param gpio_num      GPIO PIN Number
 * @param type          GPIO Interrupt Trigger Type
 * @param handler       GPIO Interrupt Handler
 * @return void
 */
void gpio_request_irq(uint32_t gpio_num, gpio_int_type_t type, gpio_irq_t handler)
{
        uint32_t set, unset;
        gpio_reg_t *reg;

        if (gpio_num >= GPIO_MAX) return;
        gpio_handler[gpio_num] = handler;

        set = (1 << GPIO_PIN(gpio_num));
        unset = ~set;
        reg = GPIO + GPIO_PORT(gpio_num);

        switch (type) {
        case GPIO_INT_LOW:
                reg->GPIO_ITRI |= set;
                reg->GPIO_IEV &= unset;
                break;
        case GPIO_INT_HIGH:
                reg->GPIO_ITRI |= set;
                reg->GPIO_IEV |= set;
                break;
        case GPIO_INT_FALLING:
                reg->GPIO_ITRI &= unset;
                reg->GPIO_IEV &= unset;
                reg->GPIO_IBE &= unset;
                break;
        case GPIO_INT_RISING:
                reg->GPIO_ITRI &= unset;
                reg->GPIO_IEV |= set;
                reg->GPIO_IBE &= unset;
                break;
        case GPIO_INT_FALLING_RISING:
                reg->GPIO_ITRI &= unset;
                reg->GPIO_IBE |= set;
                break;
        }
        return ;
}

/**
 * @fn gpio_irq_handler
 * @brief GPIO Interrupt Handler
 *
 * @return void
 */
static void gpio_irq_handler(void)
{
        uint32_t MIS;
        int i, j, base = 0;

        for (i = 0; i < 4; i ++) {
                MIS = GPIO[i].GPIO_MIS;
                GPIO[i].GPIO_IC = MIS;
                for (j = 0; j < 32; j ++) {
                        if (MIS & (1 << j)) {
                                gpio_handler[base + j]();
                        }
                }
                base += 32;
        }
}

/**
 * @fn gpio_init
 * @brief GPIO Initializer
 *
 * @return void
 */
static int gpio_init(void)
{
        request_irq(IRQ_GPIO, gpio_irq_handler);
        return 0;
}

arch_initcall(gpio_init);



